<!DOCTYPE html>
<html lang="zh-CN">
{% load static %}
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>菜单管理树形表格</title>
    <link href="{% static '/css/bootstrap.min.css' %}" rel="stylesheet">
    <link href="{% static '/css/bootstrap-icons.css' %}" rel="stylesheet">
    <script src="{% static '/js/vue.global.js' %}"></script>
    <script src="{% static 'js/axios.min.js' %}"></script>


    <style>
        body { background: #fafbfc; }
        .table > thead > tr > th, .table > tbody > tr > td {
            vertical-align: middle;
            text-align: center;
            border-left: none;
            border-right: none;
        }
        .tree-toggle {
            cursor: pointer;
            user-select: none;
            display: inline-block;
            width: 16px;
            text-align: center;
        }
        .tree-indent {
            display: inline-block;
            width: 20px;
        }
        .icon-cell {
            font-size: 18px;
        }
        .action-link {
            color: #409EFF;
            cursor: pointer;
        }
        .action-link:hover {
            text-decoration: underline;
        }
        .table-striped > tbody > tr:nth-of-type(odd) {
            background-color: #f9f9f9;
        }
        .table {
            border: none;
            table-layout: fixed;
            width: 100%;
        }
        .table > thead > tr > th {
            border-bottom: 1px solid #ddd;
            background-color: #f5f5f5;
        }
        .table > tbody > tr > td {
            border-bottom: 1px solid #ddd;
            word-break: break-all;
            overflow: hidden;
            white-space: normal;
            text-overflow: ellipsis;
        }
        .arrow-right {
            display: inline-block;
            width: 0;
            height: 0;
            border-top: 4px solid transparent;
            border-bottom: 4px solid transparent;
            border-left: 6px solid #666;
            transition: transform 0.2s;
        }
        .arrow-down {
            transform: rotate(90deg);
        }
        .bi-icon-select {
            font-size: 1.5em;
            margin: 0.25em;
            cursor: pointer;
            border: 2px solid transparent;
            border-radius: 4px;
            padding: 2px 4px;
        }
        .bi-icon-select.selected {
            border-color: #409EFF;
            background: #e6f7ff;
        }
        .modal-header {
            cursor: move;
            user-select: none;
            padding: 8px 15px !important;
            min-height: 36px;
        }
        .modal-footer {
            padding: 8px 15px !important;
        }
        .modal-dialog {
            width: 800px;
            max-width: 98vw;
            min-width: 600px;
            max-height: 90vh;
            margin: 40px auto 0 auto;
            left: 0;
            right: 0;
        }
        .modal-content {
            max-height: 90vh;
            overflow: visible;
            transform: scale(0.9);
            transform-origin: top left;
        }
        .modal-body {
            max-height: 65vh;
            overflow-y: auto;
        }
        /* 图标选择器浮层 */
        .icon-picker-pop {
            position: absolute;
            z-index: 1051;
            background: #fff;
            border: 1px solid #eee;
            border-radius: 4px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.12);
            padding: 4px 2px;
            max-width: 350px;
            max-height: 120px;
            overflow: auto;
            left: 0;
            top: 100%;
        }
        th, td {
            min-width: 60px;
        }
    </style>
</head>
{% verbatim %}
<body>
<div id="app" class="container" style="margin-top:40px;">
    <div class="clearfix" style="margin-bottom:15px;">
        <!--下拉框，input-->
        <div class="float-start" style="margin-right:10px;">
            <select class="form-select" style="width:130px;" v-model="searchForm.system_id">
                <option value="9001">后台终端系统</option>
                <option value="9002">订单系统</option>
                <option value="9003">物流系统</option>
            </select>
        </div>
      <button class="btn btn-primary float-start" @click="openAddWin"><i class="bi bi-plus"></i> 新 增</button>



    </div>
    <div style="overflow-x:auto; max-width:100%;">
      <table class="table table-striped">
        <thead>
            <tr>
                <th style="width:180px;">菜单名称</th>
                <th style="width:60px;">图标</th>
                <th style="width:50px;">排序</th>
                <th style="width:80px;">菜单类型</th>
                <th style="width:160px;">访问路径</th>
                <th style="width:80px;">权限标识</th>
                <th style="width:60px;">状态</th>
                <th style="width:150px;">创建时间</th>
                <th style="width:120px;">操作</th>
            </tr>
        </thead>
        <tbody>
            <template v-for="item in flatMenu">
                <tr v-if="item.visible" :key="item.id">
                    <td style="text-align:left;">
                        <span v-for="n in item.level" :key="n" class="tree-indent"></span>
                        <span v-if="item.children && item.children.length" class="tree-toggle" @click="toggle(item)">
                            <span class="arrow-right" :class="{ 'arrow-down': item.expanded }"></span>
                        </span>
                        <span v-else class="tree-indent"></span>
                        {{ item.menu_name }}
                    </td>
                    <td class="icon-cell">
                        <i v-if="item.icon" :class="`bi ${item.icon}`"></i>
                    </td>
                    <td>{{ item.sort }}</td>
                    <td>
                      <span v-if ="item.menu_type === 0">目录</span>
                      <span v-else-if ="item.menu_type === 1">菜单</span>
                      <span v-else-if ="item.menu_type === 2">按钮</span>
                      <span v-else-if ="item.menu_type === 3">API</span>
                    </td>
                    <td style="text-align:left;">{{ item.path }}</td>
                    <td>{{ item.permission }}</td>
                    <td>
                      <span v-if ="item.status === 1">正常</span>
                      <span v-else-if ="item.status === 01">禁用</span>
                    </td>
                    <td>{{ item.create_date }}</td>
                    <td><span class="action-link">✎修改</span></td>
                </tr>
            </template>
        </tbody>
      </table>
    </div>

    <!-- 新增菜单模态框 -->
    <div class="modal" tabindex="-1" :class="{ show: showAddModal }" :style="showAddModal ? 'display:block;' : 'display:none;'">
      <div class="modal-dialog" ref="modalDialog" :style="modalDialogStyle">
        <div class="modal-content">
          <div class="modal-header" @mousedown="startDrag">
            <h5 class="modal-title">修改菜单</h5>
            <button type="button" class="btn-close ms-auto" aria-label="Close" @click="showAddModal = false"></button>
          </div>
          <div class="modal-body">
            <form autocomplete="off">
              <div class="mb-3 row">
                <label class="col-2 col-form-label text-end">上级菜单</label>
                <div class="col-10">
                  <input type="text" class="form-control" v-model="addForm.parentName" readonly placeholder="请选择上级菜单">
                </div>
              </div>
              <div class="mb-3 row">
                <label class="col-2 col-form-label text-end">菜单类型</label>
                <div class="col-10 d-flex align-items-center">
                  <div class="form-check form-check-inline">
                    <input class="form-check-input" type="radio" value="目录" v-model="addForm.type" id="type-catalog">
                    <label class="form-check-label" for="type-catalog">目录</label>
                  </div>
                  <div class="form-check form-check-inline">
                    <input class="form-check-input" type="radio" value="菜单" v-model="addForm.type" id="type-menu">
                    <label class="form-check-label" for="type-menu">菜单</label>
                  </div>
                  <div class="form-check form-check-inline">
                    <input class="form-check-input" type="radio" value="按钮" v-model="addForm.type" id="type-btn">
                    <label class="form-check-label" for="type-btn">按钮</label>
                  </div>
                </div>
              </div>
              <div class="mb-3 row" style="position:relative;">
                <label class="col-2 col-form-label text-end">菜单图标</label>
                <div class="col-10" style="position:relative;">
                  <input type="text" class="form-control" v-model="addForm.icon" placeholder="请选择图标或输入图标名" readonly style="margin-bottom:8px;" @focus="showIconPicker = true" @click="showIconPicker = true" ref="iconInput">
                  <div v-if="showIconPicker" class="icon-picker-pop">
                    <span v-for="icon in biIcons" :key="icon" :title="icon" class="bi-icon-select" :class="{selected: addForm.icon === icon}" @click="selectIcon(icon)">
                      <i :class="'bi ' + icon"></i>
                    </span>
                  </div>
                </div>
              </div>
              <div class="mb-3 row">
                <label class="col-2 col-form-label text-end">菜单名称</label>
                <div class="col-4">
                  <input type="text" class="form-control" v-model="addForm.name" placeholder="菜单管理">
                </div>
                <label class="col-2 col-form-label text-end">显示排序</label>
                <div class="col-3">
                  <input type="number" class="form-control" v-model="addForm.order" min="0">
                </div>
              </div>
              <div class="mb-3 row">
                <label class="col-2 col-form-label text-end">是否外链</label>
                <div class="col-10 d-flex align-items-center">
                  <div class="form-check form-check-inline">
                    <input class="form-check-input" type="radio" value="是" v-model="addForm.isLink" id="islink-yes">
                    <label class="form-check-label" for="islink-yes">是</label>
                  </div>
                  <div class="form-check form-check-inline">
                    <input class="form-check-input" type="radio" value="否" v-model="addForm.isLink" id="islink-no">
                    <label class="form-check-label" for="islink-no">否</label>
                  </div>
                </div>
              </div>
              <div class="mb-3 row">
                <label class="col-2 col-form-label text-end">路由地址</label>
                <div class="col-4">
                  <input type="text" class="form-control" v-model="addForm.path" placeholder="menu">
                </div>
                <label class="col-2 col-form-label text-end">组件路径</label>
                <div class="col-3">
                  <input type="text" class="form-control" v-model="addForm.component" placeholder="vadmin/permission/menu/index">
                </div>
              </div>
              <div class="mb-3 row">
                <label class="col-2 col-form-label text-end">接口路径</label>
                <div class="col-4">
                  <input type="text" class="form-control" v-model="addForm.apiPath" placeholder="请输入后端接口路径">
                </div>
                <label class="col-2 col-form-label text-end">请求方法</label>
                <div class="col-3">
                  <select class="form-select" v-model="addForm.apiMethod">
                    <option>GET</option>
                    <option>POST</option>
                    <option>PUT</option>
                    <option>DELETE</option>
                  </select>
                </div>
              </div>
              <div class="mb-3 row">
                <label class="col-2 col-form-label text-end">权限标识</label>
                <div class="col-10">
                  <input type="text" class="form-control" v-model="addForm.permission" placeholder="请输入权限标识">
                </div>
              </div>
              <div class="mb-3 row">
                <label class="col-2 col-form-label text-end">显示状态</label>
                <div class="col-4 d-flex align-items-center">
                  <div class="form-check form-check-inline">
                    <input class="form-check-input" type="radio" value="显示" v-model="addForm.visible" id="visible-yes">
                    <label class="form-check-label" for="visible-yes">显示</label>
                  </div>
                  <div class="form-check form-check-inline">
                    <input class="form-check-input" type="radio" value="隐藏" v-model="addForm.visible" id="visible-no">
                    <label class="form-check-label" for="visible-no">隐藏</label>
                  </div>
                </div>
                <label class="col-2 col-form-label text-end">菜单状态</label>
                <div class="col-4 d-flex align-items-center">
                  <div class="form-check form-check-inline">
                    <input class="form-check-input" type="radio" value="正常" v-model="addForm.status" id="status-normal">
                    <label class="form-check-label" for="status-normal">正常</label>
                  </div>
                  <div class="form-check form-check-inline">
                    <input class="form-check-input" type="radio" value="停用" v-model="addForm.status" id="status-stop">
                    <label class="form-check-label" for="status-stop">停用</label>
                  </div>
                </div>
              </div>
              <div class="mb-3 row">
                <label class="col-2 col-form-label text-end">是否缓存</label>
                <div class="col-10 d-flex align-items-center">
                  <div class="form-check form-check-inline">
                    <input class="form-check-input" type="radio" value="不缓存" v-model="addForm.cache" id="cache-no">
                    <label class="form-check-label" for="cache-no">不缓存</label>
                  </div>
                  <div class="form-check form-check-inline">
                    <input class="form-check-input" type="radio" value="缓存" v-model="addForm.cache" id="cache-yes">
                    <label class="form-check-label" for="cache-yes">缓存</label>
                  </div>
                </div>
              </div>
            </form>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-primary" @click="showAddModal = false">确定</button>
            <button type="button" class="btn btn-secondary" @click="showAddModal = false">取消</button>
          </div>
        </div>
      </div>
    </div>
</div>
<script src="/static/js/vue.global.js"></script>
<script>
const { createApp, ref, reactive, onMounted } = Vue;

// Bootstrap5常用图标集（可根据需要增减）
const biIcons = [
  'bi-house', 'bi-gear', 'bi-person', 'bi-list', 'bi-lock', 'bi-briefcase', 'bi-diagram-3', 'bi-menu-app',
  'bi-collection', 'bi-eye', 'bi-wrench', 'bi-plus', 'bi-pencil', 'bi-trash', 'bi-check', 'bi-x', 'bi-arrow-right', 'bi-arrow-down', 'bi-folder', 'bi-star', 'bi-search', 'bi-bell', 'bi-calendar', 'bi-flag', 'bi-globe', 'bi-lightning', 'bi-link', 'bi-box-arrow-up-right', 'bi-box-arrow-in-right', 'bi-box-arrow-in-left', 'bi-box-arrow-up-left', 'bi-box-arrow-down-right', 'bi-box-arrow-down-left', 'bi-box-arrow-up', 'bi-box-arrow-down', 'bi-box-arrow-left', 'bi-box-arrow-right'
];

// 菜单数据全部改为bi-xxx风格
const menuData = [
  {
    id: 1,
    name: '首页',
    icon: 'bi-house',
    order: 0,
    type: '目录',
    permission: '',
    path: '',
    status: '正常',
    updateTime: '2021-05-02 19:12:06',
    createTime: '2021-05-02 18:58:06',
    children: []
  },
  {
    id: 2,
    name: '系统管理',
    icon: 'bi-gear',
    order: 1,
    type: '目录',
    permission: '',
    path: '',
    status: '正常',
    updateTime: '2021-02-27 07:50:00',
    createTime: '2021-02-27 07:41:28',
    children: [
      {
        id: 3,
        name: '权限管理',
        icon: 'bi-lock',
        order: 2,
        type: '目录',
        permission: '',
        path: '',
        status: '正常',
        updateTime: '2021-02-27 07:51:14',
        createTime: '2021-02-27 07:51:14',
        children: [
          {
            id: 4,
            name: '岗位管理',
            icon: 'bi-briefcase',
            order: 1,
            type: '菜单',
            permission: '',
            path: 'vadmin/permission/post/',
            status: '正常',
            updateTime: '2021-03-26 00:49:19',
            createTime: '2021-02-27 08:25:37',
            children: []
          },
          {
            id: 5,
            name: '部门管理',
            icon: 'bi-diagram-3',
            order: 2,
            type: '菜单',
            permission: '',
            path: 'vadmin/permission/dept/',
            status: '正常',
            updateTime: '2021-03-26 00:49:25',
            createTime: '2021-02-27 08:26:48',
            children: []
          },
          {
            id: 6,
            name: '菜单管理',
            icon: 'bi-list',
            order: 3,
            type: '菜单',
            permission: '',
            path: 'vadmin/permission/menu/',
            status: '正常',
            updateTime: '2021-03-26 00:49:31',
            createTime: '2021-02-27 08:28:20',
            children: [
              {
                id: 7,
                name: '菜单查询',
                icon: 'bi-search',
                order: 0,
                type: '按钮',
                permission: 'permission:menus:get',
                path: '',
                status: '正常',
                updateTime: '2021-03-09 17:21:56',
                createTime: '2021-03-09 17:21:47',
                children: []
              },
              {
                id: 8,
                name: '菜单新增',
                icon: 'bi-plus',
                order: 1,
                type: '按钮',
                permission: 'permission:menus:post',
                path: '',
                status: '正常',
                updateTime: '2021-03-03 15:00:41',
                createTime: '2021-03-03 15:00:41',
                children: []
              },
              {
                id: 9,
                name: '菜单修改',
                icon: 'bi-pencil',
                order: 2,
                type: '按钮',
                permission: 'permission:menus:{id}:put',
                path: '',
                status: '正常',
                updateTime: '2021-03-03 15:01:03',
                createTime: '2021-03-03 15:01:03',
                children: []
              },
              {
                id: 10,
                name: '菜单删除',
                icon: 'bi-trash',
                order: 3,
                type: '按钮',
                permission: 'permission:menus:{id}:delete',
                path: '',
                status: '正常',
                updateTime: '2021-03-03 15:02:56',
                createTime: '2021-03-03 15:02:56',
                children: []
              }
            ]
          }
        ]
      },
      {
        id: 11,
        name: '角色管理',
        icon: 'bi-person',
        order: 4,
        type: '菜单',
        permission: '',
        path: 'vadmin/permission/role/index',
        status: '正常',
        updateTime: '2021-03-26 00:49:37',
        createTime: '2021-02-27 08:29:30',
        children: []
      },
      {
        id: 12,
        name: '用户管理',
        icon: 'bi-person',
        order: 5,
        type: '菜单',
        permission: '',
        path: 'vadmin/permission/user/index',
        status: '正常',
        updateTime: '2021-03-26 00:49:42',
        createTime: '2021-02-27 08:30:14',
        children: []
      }
    ]
  },
  {
    id: 13,
    name: '系统监控',
    icon: 'bi-eye',
    order: 3,
    type: '目录',
    permission: '',
    path: '',
    status: '正常',
    updateTime: '2021-03-16 14:33:05',
    createTime: '2021-03-16 14:33:05',
    children: []
  },
  {
    id: 14,
    name: '系统工具',
    icon: 'bi-wrench',
    order: 4,
    type: '目录',
    permission: '',
    path: '',
    status: '正常',
    updateTime: '2021-03-16 15:15:21',
    createTime: '2021-03-16 15:15:21',
    children: []
  }
];

function flattenMenu(tree, level = 0, parentExpanded = true) {
  let arr = [];
  for (const node of tree) {
    node.level = level;
    node.expanded = node.expanded ?? false;
    node.visible = parentExpanded;
    arr.push(node);
    if (node.children && node.children.length) {
      arr = arr.concat(flattenMenu(node.children, level + 1, node.expanded && node.visible));
    }
  }
  return arr;
}

createApp({
  setup() {
    /**搜索框*/
    const searchForm=reactive({
      system_id:"9001",
    });
    const menuTree = reactive([]);
    const flatMenu = ref([]);
    const showAddModal = ref(false);
    const showIconPicker = ref(false);
    const addForm = reactive({
      parentName: '',
      type: '菜单',
      icon: '',
      name: '',
      order: 0,
      isLink: '否',
      path: '',
      component: '',
      apiPath: '',
      apiMethod: 'GET',
      permission: '',
      visible: '显示',
      status: '正常',
      cache: '不缓存'
    });
    const dragging = ref(false);
    const dragOffset = reactive({x:0, y:0});
    const modalDialogStyle = ref({});

    function updateFlatMenu() {
      const setVisible = (nodes, parentVisible) => {
        for (const node of nodes) {
          node.visible = parentVisible;
          if (node.children && node.children.length) {
            setVisible(node.children, parentVisible && node.expanded);
          }
        }
      };
      setVisible(menuTree, true);
      flatMenu.value = flattenMenu(menuTree);
      console.log("=============>flatMenu",flatMenu.value);
    }

    function toggle(item) {
      item.expanded = !item.expanded;
      updateFlatMenu();
    }

    function selectIcon(icon) {
      addForm.icon = icon;
      showIconPicker.value = false;
    }

    function handleClickOutside(e) {
      const input = document.querySelector('[ref=iconInput]');
      const picker = document.querySelector('.icon-picker-pop');
      if (input && !input.contains(e.target) && (!picker || !picker.contains(e.target))) {
        showIconPicker.value = false;
      }
    }

    function startDrag(e) {
      if (e.button !== 0) return;
      dragging.value = true;
      const dialog = document.querySelector('.modal-dialog');
      const rect = dialog.getBoundingClientRect();
      dragOffset.x = e.clientX - rect.left;
      dragOffset.y = e.clientY - rect.top;
      document.addEventListener('mousemove', onDrag);
      document.addEventListener('mouseup', stopDrag);
    }

    function onDrag(e) {
      if (!dragging.value) return;
      let left = e.clientX - dragOffset.x;
      let top = e.clientY - dragOffset.y;
      left = Math.max(0, Math.min(window.innerWidth - 900, left));
      top = Math.max(0, Math.min(window.innerHeight - 100, top));
      modalDialogStyle.value = {
        position: 'fixed',
        left: left + 'px',
        top: top + 'px',
        margin: 0,
        zIndex: 1050,
        width: '900px',
        maxWidth: '98vw',
        minWidth: '600px',
        maxHeight: '90vh'
      };
    }

    function stopDrag() {
      dragging.value = false;
      document.removeEventListener('mousemove', onDrag);
      document.removeEventListener('mouseup', stopDrag);
    }

    function openAddWin() {
      showAddModal.value = true;
    }

    /**查询菜单数据*/
    const queryList=()=>{
      axios({
        method:'get',
        url:'/demo/menu/tree_data/',
        params:searchForm
      }).then(res=>{
        console.log("=============>data:",res.data);
        if(res.data.code===200){

          menuTree.splice(0,menuTree.length,...res.data.data);
          console.log("=============>menuTree",menuTree);
          updateFlatMenu();
        }
      })
    }

    onMounted(() => {
      queryList();
      updateFlatMenu();
    });

    return {
      menuTree,
      flatMenu,
      showAddModal,
      showIconPicker,
      addForm,
      biIcons,
      dragging,
      dragOffset,
      modalDialogStyle,
      toggle,
      updateFlatMenu,
      selectIcon,
      handleClickOutside,
      onDrag,
      stopDrag,
      openAddWin,
      queryList,
      searchForm,
      startDrag,
    };
  }
}).mount('#app');
</script>
</body>
 {% endverbatim %} <!-- 添加此行 -->
</html>